/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2016 Broadcom Corporation
 */

#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/bmips.h>

#include "pm.h"

	.text
	.set		noreorder
	.align		5
	.global		s3_reentry

/*
 * a0: AON_CTRL base register
 * a1: D-Cache line size
 */
LEAF(brcm_pm_do_s3)

	/* Get the address of s3_context */
	la	t0, gp_regs
	sw	ra, 0(t0)
	sw	s0, 4(t0)
	sw	s1, 8(t0)
	sw	s2, 12(t0)
	sw	s3, 16(t0)
	sw	s4, 20(t0)
	sw	s5, 24(t0)
	sw	s6, 28(t0)
	sw	s7, 32(t0)
	sw	gp, 36(t0)
	sw	sp, 40(t0)
	sw	fp, 44(t0)

	/* Save CP0 Status */
	mfc0	t1, CP0_STATUS
	sw	t1, 48(t0)

	/* Write-back gp registers - cache will be gone */
	addiu	t1, a1, -1
	not	t1
	and	t0, t1

	/* Flush at least 64 bytes */
	addiu	t2, t0, 64
	and	t2, t1

1:	cache	0x17, 0(t0)
	bne	t0, t2, 1b
	addu	t0, a1

	/* Drop to deep standby */
	li	t1, PM_WARM_CONFIG
	sw	zero, AON_CTRL_PM_CTRL(a0)
	lw	zero, AON_CTRL_PM_CTRL(a0)
	sw	t1, AON_CTRL_PM_CTRL(a0)
	lw	t1, AON_CTRL_PM_CTRL(a0)

	li	t1, (PM_WARM_CONFIG | PM_PWR_DOWN)
	sw	t1, AON_CTRL_PM_CTRL(a0)
	lw	t1, AON_CTRL_PM_CTRL(a0)

	/* Enable CP0 interrupt 2 and wait for interrupt */
	mfc0	t0, CP0_STATUS

	li	t1, ~(ST0_IM | ST0_IE)
	and	t0, t1
	ori	t0, STATUSF_IP2
	mtc0	t0, CP0_STATUS
	nop
	nop
	nop
	ori	t0, ST0_IE
	mtc0	t0, CP0_STATUS

        /* Wait for interrupt */
        wait
        nop

s3_reentry:

	/* Clear call/return stack */
	li	t0, (0x06 << 16)
	mtc0	t0, $22, 2
	ssnop
	ssnop
	ssnop

	/* Clear jump target buffer */
	li	t0, (0x04 << 16)
	mtc0	t0, $22, 2
	ssnop
	ssnop
	ssnop

	sync
	nop

	/* Setup mmu defaults */
	mtc0	zero, CP0_WIRED
	mtc0	zero, CP0_ENTRYHI
	li	k0, PM_DEFAULT_MASK
	mtc0	k0, CP0_PAGEMASK

	li	sp, BMIPS_WARM_RESTART_VEC
	la	k0, plat_wired_tlb_setup
	jalr	k0
	nop

	/* Restore general purpose registers */
	la	t0, gp_regs
	lw	fp, 44(t0)
	lw	sp, 40(t0)
	lw	gp, 36(t0)
	lw	s7, 32(t0)
	lw	s6, 28(t0)
	lw	s5, 24(t0)
	lw	s4, 20(t0)
	lw	s3, 16(t0)
	lw	s2, 12(t0)
	lw	s1, 8(t0)
	lw	s0, 4(t0)
	lw	ra, 0(t0)

	/* Restore CP0 status */
	lw	t1, 48(t0)
	mtc0	t1, CP0_STATUS

	/* Return to caller */
	li	v0, 0
	jr      ra
	nop

END(brcm_pm_do_s3)
